package it.polimi.traveldream.managers;

import it.polimi.traveldream.managers.credentials.UserCredentialManager;
import it.polimi.traveldream.persistence.Pacchetto;
import it.polimi.traveldream.persistence.dtos.HotelProdDTO;
import it.polimi.traveldream.persistence.Aeroporto;
import it.polimi.traveldream.persistence.Compagnia;
import it.polimi.traveldream.persistence.PonteHotelPacchetto;
import it.polimi.traveldream.persistence.Hotel;
import it.polimi.traveldream.persistence.Utente;
import it.polimi.traveldream.persistence.PonteTourPacchetto;
import it.polimi.traveldream.persistence.PonteTrattaPacchetto;
import it.polimi.traveldream.persistence.Tour;
import it.polimi.traveldream.persistence.TrattaVolo;
import it.polimi.traveldream.persistence.dtos.AeroportoDTO;
import it.polimi.traveldream.persistence.dtos.CompagniaDTO;
import it.polimi.traveldream.persistence.dtos.PacchettoDTO;
import it.polimi.traveldream.persistence.dtos.PonteHotelPaccDTO;
import it.polimi.traveldream.persistence.dtos.PonteTourPaccDTO;
import it.polimi.traveldream.persistence.dtos.PonteTrattaVoloDTO;
import it.polimi.traveldream.persistence.dtos.TourProdDTO;
import it.polimi.traveldream.persistence.dtos.TrattaProdDTO;
import it.polimi.traveldream.persistence.dtos.LocalitàDTO;
import it.polimi.traveldream.persistence.Localita;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class PacchettoManagerImpl implements PacchettoManager {

	@PersistenceContext
	private EntityManager em;

	@EJB(beanName = "UserCredentialManagerImpl")
	private UserCredentialManager userMgr;

	@EJB(beanName = "ProductManagerImpl")
	private ProductManager prodMgr;

	private ProductManagerImpl getProdManagerImpl() {
		return (ProductManagerImpl) prodMgr;
	}

	private UserCredentialManagerImpl getUserCredManager() {
		return (UserCredentialManagerImpl) userMgr;
	}

	public Utente findUtenteEntity(String email) {
		return em.find(Utente.class, email);
	}

	// ---------------------------------------------------- !!SALVATAGGIO
	// PACCHETTO IN DB!! ------------------------------------
	/**
	 * QUESTO è il save pacchetto che inserisce nel DB il pacchetto con
	 * all'interno le liste di ponti.
	 */
	@Override
	public void savePacchetto(PacchettoDTO pacchettoDTO) {

		// Converto il pacchetto DTO che mi viene passato in pacchetto entity,
		// senza però aggiungere le liste di ponti
		Pacchetto pacchetto = convert(pacchettoDTO);

		// qui chiamo il metodo che inserisce le liste di ponti convertite in
		// entity nel Pacchetto
		insertPonti(pacchetto, pacchettoDTO);

		// salvo il pacchetto nel DB
		em.persist(pacchetto);

	}

	// ---------------------------------------------------- TOUR TOUR TOUR

	// Per hotel l'avevo fatto con il metodo, a quanto pare sbagliato, di
	// salvare il pacchetto senza liste ponte
	// e di aggiornarlo poi con le liste ponte, ma non andava proprio.
	// Per la tratta non l'ho fatto

	/**
	 * Metodo che converte la lista di pontiTourDTO in entity e la setta nel
	 * pacchetto
	 * 
	 * @param pacchetto
	 * @param pacchettoDTO
	 */
	public void insertPonti(Pacchetto pacchetto, PacchettoDTO pacchettoDTO) {
		// Creo una lista di PontiTourPacchetto di supporto
		List<PonteTourPacchetto> ponteTour = new ArrayList<PonteTourPacchetto>();

		// converto il ponteTourDTO in entity
		convertPTourDTO(pacchettoDTO.getPonteTour(), ponteTour, pacchetto);

		// setto il ponte entity nel Pacchetto

		pacchetto.setPonteTourPacchettos(ponteTour);

		// Per Ponti Hotel
		List<PonteHotelPacchetto> ponteHotel = new ArrayList<PonteHotelPacchetto>();

		convertPHotelDTO(pacchettoDTO.getPonteHotel(), ponteHotel, pacchetto);

		pacchetto.setPonteHotelPacchettos(ponteHotel);

		// Per Ponti Tratte
		List<PonteTrattaPacchetto> ponteTratta = new ArrayList<PonteTrattaPacchetto>();

		convertPTrattaDTO(pacchettoDTO.getPonteVolo(), ponteTratta, pacchetto);

		pacchetto.setPonteTrattaPacchettos(ponteTratta);
	}

	/**
	 * Metodo che converte la il ponteTourDTO in entity
	 * 
	 * @param ponteTourDTO
	 * @param ponteTour
	 * @param pacchetto
	 */
	public void convertPTourDTO(List<PonteTourPaccDTO> ponteTourDTO,
			List<PonteTourPacchetto> ponteTour, Pacchetto pacchetto) {
		PonteTourPacchetto ponte = new PonteTourPacchetto();
		if (ponteTourDTO.size() > 0) {
			for (PonteTourPaccDTO ponteDTO : ponteTourDTO) {
				ponte.setNumeroPersone(ponteDTO.getNumeroPersone());
				ponte.setTour(em.find(Tour.class, ponteDTO.getTour().getID()));
				ponte.setPacchetto(pacchetto);
				ponteTour.add(ponte);
			}
		}
	}

	public void convertPHotelDTO(List<PonteHotelPaccDTO> list,
			List<PonteHotelPacchetto> ponteHotel, Pacchetto pacchetto) {
		PonteHotelPacchetto ponte;

		for (PonteHotelPaccDTO ponteDTO : list) {
			ponte = new PonteHotelPacchetto();
			ponte.setNumeroPersone(ponteDTO.getNumeroPersone());
			ponte.setHotel(em.find(Hotel.class, ponteDTO.getHotel().getID()));
			em.clear();
			ponte.setPacchetto(pacchetto);
			ponteHotel.add(ponte);
		}
	}

	public void convertPTrattaDTO(List<PonteTrattaVoloDTO> ponteTrattaDTO,
			List<PonteTrattaPacchetto> ponteTratta, Pacchetto pacchetto) {
		PonteTrattaPacchetto ponte = new PonteTrattaPacchetto();

		for (PonteTrattaVoloDTO ponteDTO : ponteTrattaDTO) {
			ponte.setNumeroPersone(ponteDTO.getNumeroPersone());
			ponte.setTrattaVolo(em.find(TrattaVolo.class, ponteDTO
					.getTrattaVolo().getID()));
			ponte.setPacchetto(pacchetto);
			ponteTratta.add(ponte);
		}
	}

	// FIN QUI PER IL SALVATAGGIO NEL DB DI UN PACCHETTO
	// --------------------------------------------------------------------------------------

	// UPDATE PACCHETTO

	public void updatePacchetto(PacchettoDTO pacchettoDTO) {
		Pacchetto pacchetto = findPacchettoEnt(pacchettoDTO.getID());
		updateEntityBasicFields(pacchetto, pacchettoDTO);
		insertPonti(pacchetto, pacchettoDTO);

		em.merge(pacchetto);
	}

	@Override
	public void updateBasicInformationsPacchetto(PacchettoDTO pacchettoDTO) {
		Pacchetto pacchetto = findPacchettoEntity(pacchettoDTO.getID());
		updateEntityBasicFields(pacchettoDTO);
		em.merge(pacchetto);
	}

	// --------------------------------------------------------------------------------------

	@Override
	public void deletePacchetto(int pacchettoDTOID) {
		em.remove(findPacchettoEntity(pacchettoDTOID));
		em.flush();
	}

	@Override
	public PacchettoDTO findPacchetto(int pacchettoDTOID) {
		return convert(findPacchettoEntity(pacchettoDTOID));
	}

	public List<HotelProdDTO> getPackHotels(int id) {
		Pacchetto pack = findPacchettoEnt(id);
		List<HotelProdDTO> hotels = new LinkedList<HotelProdDTO>();

		List<PonteHotelPacchetto> ponteH = pack.getPonteHotelPacchettos();
		for (PonteHotelPacchetto ponteHP : ponteH) {
			Hotel hot = ponteHP.getHotel();
			hotels.add(convertiHotel(hot));
		}
		return hotels;
	}

	public List<TourProdDTO> getPackTours(int id) {
		Pacchetto pack = findPacchettoEnt(id);
		List<TourProdDTO> tours = new ArrayList<TourProdDTO>();
		List<PonteTourPacchetto> ponteTour = pack.getPonteTourPacchettos();
		for (PonteTourPacchetto ponteTP : ponteTour) {
			Tour tou = ponteTP.getTour();
			tours.add(convertTour(tou));
		}
		return tours;
	}

	public List<TrattaProdDTO> getTratteTours(int id) {
		Pacchetto pack = findPacchettoEnt(id);
		List<TrattaProdDTO> tratte = new ArrayList<TrattaProdDTO>();
		List<PonteTrattaPacchetto> ponteTratta = pack
				.getPonteTrattaPacchettos();
		for (PonteTrattaPacchetto ponteTP : ponteTratta) {
			TrattaVolo tra = ponteTP.getTrattaVolo();
			tratte.add(convertTratta(tra));
		}
		return tratte;
	}

	public Pacchetto findPacchettoEnt(int id) {
		return em.find(Pacchetto.class, id);
	}

	@Override
	public List<PacchettoDTO> findAllPacchetto() {
		List<Pacchetto> pacchetti = em.createNamedQuery(Pacchetto.FIND_ALL,
				Pacchetto.class).getResultList();
		return ConverterFactory
				.convertList(
						pacchetti,
						new ConverterFactory.ConverterCallback<Pacchetto, PacchettoDTO>() {

							@Override
							public PacchettoDTO autoConvert(Pacchetto dto) {
								return convert(dto);
							}

						});
	}
	
	public List<PacchettoDTO> findPubPacchetto() {
		List<Pacchetto> pacchetti = em.createNamedQuery(Pacchetto.FIND_PUB,
				Pacchetto.class).getResultList();
		return ConverterFactory
				.convertList(
						pacchetti,
						new ConverterFactory.ConverterCallback<Pacchetto, PacchettoDTO>() {

							@Override
							public PacchettoDTO autoConvert(Pacchetto dto) {
								return convert(dto);
							}

						});
	}

	public Pacchetto findPacchettoEntity(int pacchettoID) {
		em.clear();
		return em.find(Pacchetto.class, pacchettoID);
	}

	public PacchettoDTO convert(Pacchetto pacchetto) {
		PacchettoDTO pacchettoDTO = new PacchettoDTO();
		pacchettoDTO.setDescrizione(pacchetto.getDescrizione());
		pacchettoDTO.setEmailImpiegato(pacchetto.getUtente().getEmail());
		pacchettoDTO.setIDPacchetto(pacchetto.getID());
		pacchettoDTO.setNome(pacchetto.getNome());
		pacchettoDTO.setPubblico(pacchetto.getPubblico());
		return pacchettoDTO;
	}

	public Pacchetto convert(PacchettoDTO pacchettoDTO) {
		Pacchetto pacchettoE = new Pacchetto();
		pacchettoE.setDescrizione(pacchettoDTO.getDescrizione());
		pacchettoE.setNome(pacchettoDTO.getNome());
		pacchettoE.setPubblico(pacchettoDTO.getPubblico());
		Utente user = findUtenteEntity(pacchettoDTO.getEmailImpiegato());
		pacchettoE.setUtente(user);

		return pacchettoE;
	}

	private void updateEntityBasicFields(Pacchetto pacchetto,
			PacchettoDTO pacchettoDTO) {
		pacchetto.setNome(pacchettoDTO.getNome());
		pacchetto.setDescrizione(pacchettoDTO.getDescrizione());
		pacchetto.setPubblico(pacchettoDTO.getPubblico());
		Utente user = findUtenteEntity(pacchettoDTO.getEmailImpiegato());
		pacchetto.setUtente(user);
	}

	private Pacchetto updateEntityBasicFields(PacchettoDTO pacchettoDTO) {
		Pacchetto p = null;

		return p;
	}

	public Hotel findHotelEntity(int hotelID) {
		return em.find(Hotel.class, hotelID);
	}

	public Localita findLocalitaEntity(int localitàID) {
		return em.find(Localita.class, localitàID);
	}

	public Aeroporto findAeroportoEntity(int aeroportoID) {
		return em.find(Aeroporto.class, aeroportoID);
	}

	public Compagnia findCompagniaEntity(int compagniaID) {
		return em.find(Compagnia.class, compagniaID);
	}

	// Hotels
	public List<HotelProdDTO> findHotelInPacchetto(PacchettoDTO pacchettoDTO) {
		Pacchetto p = em.find(Pacchetto.class, pacchettoDTO.getID());
		List<PonteHotelPacchetto> pontePacchetto = p.getPonteHotelPacchettos();
		List<HotelProdDTO> hotelsDTO = new ArrayList<HotelProdDTO>();
		for (PonteHotelPacchetto ponte : pontePacchetto) {
			PonteHotelPaccDTO dto = new PonteHotelPaccDTO();
			HotelProdDTO dtoHotel = convertiHotel(ponte.getHotel());
			hotelsDTO.add(dtoHotel);
		}
		return hotelsDTO;
	}

	// Hotel
	public List<HotelProdDTO> findHotelInPacchetto(int IDPacchetto) {
		Pacchetto p = em.find(Pacchetto.class, IDPacchetto);
		List<PonteHotelPacchetto> pontePacchetto = p.getPonteHotelPacchettos();
		List<HotelProdDTO> hotelsDTO = new ArrayList<HotelProdDTO>();
		for (PonteHotelPacchetto ponte : pontePacchetto) {
			PonteHotelPaccDTO dto = new PonteHotelPaccDTO();
			HotelProdDTO dtoHotel = convertiHotel(ponte.getHotel());
			// dtoHotel.setLocalita(convertiLocalita(ponte.getHotel().getLocalita()));
			hotelsDTO.add(dtoHotel);
		}
		return hotelsDTO;
	}

	public List<PonteHotelPaccDTO> findPonteHotelInPacchetto(int IDPacchetto) {
		Pacchetto p = em.find(Pacchetto.class, IDPacchetto);
		List<PonteHotelPacchetto> pontePacchetto = p.getPonteHotelPacchettos();
		List<PonteHotelPaccDTO> ponteDTO = new ArrayList<PonteHotelPaccDTO>();
		for (PonteHotelPacchetto ponte : pontePacchetto) {
			ponteDTO.add(convertiPonteHotel(ponte));
		}
		return ponteDTO;
	}

	public PonteHotelPaccDTO convertiPonteHotel(PonteHotelPacchetto ponte) {
		PonteHotelPaccDTO ponteDTO = new PonteHotelPaccDTO();
		ponteDTO.setHotel(convertiHotel(ponte.getHotel()));
		ponteDTO.setNumeroPersone(ponte.getNumeroPersone());
		return ponteDTO;
	}

	public PonteHotelPacchetto convertiPonteHotelDTO(PonteHotelPaccDTO ponte) {
		PonteHotelPacchetto ponteHotelEntity = new PonteHotelPacchetto();
		ponteHotelEntity.setHotel(getProdManagerImpl()
				.convert(ponte.getHotel()));
		ponteHotelEntity.setNumeroPersone(ponte.getNumeroPersone());
		return ponteHotelEntity;
	}

	public HotelProdDTO convertiHotel(Hotel hotel) {
		HotelProdDTO hotelDTO = new HotelProdDTO();
		hotelDTO.setID(hotel.getIDHotel());
		hotelDTO.setEmailImpiegato(hotel.getUtente().getEmail());
		hotelDTO.setLocalitàID(hotel.getLocalita().getIDLocalita());
		hotelDTO.setNome(hotel.getNome());
		hotelDTO.setNumeroCivico(hotel.getNumeroCivico());
		hotelDTO.setVia(hotel.getVia());
		hotelDTO.setLocalita(convertiLocalita(hotel.getLocalita()));
		return hotelDTO;
	}

	public LocalitàDTO convertiLocalita(Localita loc) {
		LocalitàDTO locDTO = new LocalitàDTO();
		locDTO.setID(loc.getIDLocalita());
		locDTO.setNome(loc.getNome());
		locDTO.setDescrizione(loc.getDescrizione());
		locDTO.setStato(loc.getStato());
		locDTO.setProvincia(loc.getProvincia());
		return locDTO;
	}

	// Tour
	public List<PonteTourPaccDTO> findPonteTourInPacchetto(int IDPacchetto) {
		List<PonteTourPaccDTO> ponteTourDTO = new ArrayList<PonteTourPaccDTO>();
		Pacchetto pack = new Pacchetto();
		pack = em.find(Pacchetto.class, IDPacchetto);
		for (PonteTourPacchetto ponte : pack.getPonteTourPacchettos()) {
			PonteTourPaccDTO ponteDTO = convertPonteTour(ponte);
			ponteTourDTO.add(ponteDTO);
		}
		return ponteTourDTO;
	}

	public PonteTourPaccDTO convertPonteTour(PonteTourPacchetto ponte) {
		PonteTourPaccDTO ponteDTO = new PonteTourPaccDTO();
		ponteDTO.setIDPonteTour(ponte.getId().getIDPacchetto());
		ponteDTO.setNumeroPersone(ponte.getNumeroPersone());
		ponteDTO.setTour(convertTour(ponte.getTour()));
		return ponteDTO;
	}

	public TourProdDTO convertTour(Tour tour) {
		TourProdDTO tourDTO = new TourProdDTO();
		tourDTO.setDescrizione(tour.getDescrizione());
		tourDTO.setEmailImpiegato(tour.getUtente().getEmail());
		tourDTO.setID(tour.getIDTour());
		tourDTO.setLocalita(convertiLocalita(tour.getLocalita()));
		return tourDTO;
	}

	public PonteTourPacchetto convertiPonteTourDTO(PonteTourPaccDTO ponte) {
		PonteTourPacchetto ponteTourEntity = new PonteTourPacchetto();
		ponteTourEntity.setTour(getProdManagerImpl().convert(ponte.getTour()));
		ponteTourEntity.setNumeroPersone(ponte.getNumeroPersone());
		return ponteTourEntity;
	}

	// Volo
	public List<PonteTrattaVoloDTO> findPonteVoloInPacchetto(int IDPacchetto) {
		List<PonteTrattaVoloDTO> ponteVoloDTO = new ArrayList<PonteTrattaVoloDTO>();
		Pacchetto pack = new Pacchetto();
		pack = em.find(Pacchetto.class, IDPacchetto);
		for (PonteTrattaPacchetto ponte : pack.getPonteTrattaPacchettos()) {
			PonteTrattaVoloDTO ponteDTO = convertPonteVolo(ponte);
			ponteVoloDTO.add(ponteDTO);
		}
		return ponteVoloDTO;
	}

	public PonteTrattaVoloDTO convertPonteVolo(PonteTrattaPacchetto tratta) {
		PonteTrattaVoloDTO trattaDTO = new PonteTrattaVoloDTO();
		trattaDTO.setNumeroPersone(tratta.getNumeroPersone());
		trattaDTO.setTrattaVolo(convertTratta(tratta.getTrattaVolo()));
		return trattaDTO;
	}

	public PonteTrattaPacchetto convertiPonteVoloDTO(PonteTrattaVoloDTO ponteDTO) {
		PonteTrattaPacchetto ponteVoloEntity = new PonteTrattaPacchetto();
		ponteVoloEntity.setTrattaVolo(getProdManagerImpl().convert(
				ponteDTO.getTrattaVolo()));
		ponteVoloEntity.setNumeroPersone(ponteDTO.getNumeroPersone());
		return ponteVoloEntity;
	}

	public TrattaProdDTO convertTratta(TrattaVolo pTratta) {
		TrattaProdDTO trattaDTO = new TrattaProdDTO();
		trattaDTO.setEmailImpiegato(pTratta.getUtente().getEmail());
		trattaDTO.setCompagnia(convertCompagnia(pTratta.getCompagnia()));
		trattaDTO.setaAeroporto(convertAeroporto(pTratta.getAdAeroporto()));
		trattaDTO.setDaAeroporto(convertAeroporto(pTratta.getDaAeroporto()));
		trattaDTO.setID(pTratta.getIDTratta());
		return trattaDTO;
	}

	public CompagniaDTO convertCompagnia(Compagnia compagnia) {
		CompagniaDTO compagniaDTO = new CompagniaDTO();
		compagniaDTO.setNome(compagnia.getNome());
		compagniaDTO.setID(compagnia.getIDCompagnia());
		return compagniaDTO;
	}

	public AeroportoDTO convertAeroporto(Aeroporto aeroporto) {
		AeroportoDTO aeroportoDTO = new AeroportoDTO();
		aeroportoDTO.setID(aeroporto.getIDAeroporto());
		aeroportoDTO.setIDlocalità(aeroporto.getLocalita().getIDLocalita());
		aeroportoDTO.setLocalita(convertiLocalita(aeroporto.getLocalita()));
		aeroportoDTO.setName(aeroporto.getNome());
		return aeroportoDTO;
	}

	public Tour findTourEntity(int pTourID) {
		return em.find(Tour.class, pTourID);
	}
}
